from constants import *

#Windows:
def init_windows():
	for idx,name in enumerate(window_names):
		cv2.namedWindow(name)
		cv2.cv.MoveWindow(name, 200*idx, 100)
 

def rad2deg(theta):
	return 180. / math.pi * theta
#Boxes:
def coords_to_rect(coords):
	return [coords[X1],coords[Y1],coords[X2]-coords[X1],coords[Y2]-coords[Y1]] # x y width height
def rect_to_coords(rect):
	return [rect[X1],rect[Y1],rect[X1] + rect[WIDTH],rect[Y1] + rect[HEIGHT]] # x1 x2 y1 y2
def rect_size(rect):
	if rect == None:
		return 0
	return float(rect[WIDTH]*rect[HEIGHT])
def inside_coords(rect=None, area=None, contour=None):
	if contour != None:
		rect = cv2.boundingRect(contour)
	return rect[X] > area[X1] and rect[X]+rect[WIDTH] < area[X2] and rect[Y] > area[Y1] and rect[Y]+rect[HEIGHT] < area[Y2]
def inside_rect(point=None, rect=None):
	return point[X] > rect[X] and point[X] < rect[X]+rect[WIDTH] and point[X] > rect[Y] and point[Y] < rect[Y]+rect[HEIGHT]
def rect_centre(rect):
	return (rect[X] + 0.5*rect[WIDTH], rect[Y] + 0.5*rect[HEIGHT])
def rect_dist(rect1, rect2):
	rect1_centre = rect_centre(rect1)
	rect2_centre = rect_centre(rect2)
	return math.sqrt((rect1_centre[X] - rect2_centre[X])**2+(rect1_centre[Y] - rect2_centre[Y])**2)
def max_rect(rects):
	maxrect = [0,0,0,0] #x y width height
	max_idx = -1
	for idx,rect in enumerate(rects):
		if rect_size(rect) > rect_size(maxrect):
			maxrect = rect
			max_idx = idx
	return (max_idx,maxrect)
def closest_rect(rects, last_rec):
	min_dist = 999999999
	closest_rect = rects[0]
	closest_idx = 0
	for idx,rect in enumerate(rects):
		dist = rect_dist(rect, last_rec)
		if dist < min_dist:
			closest_rect = rect
			min_dist = dist
			closest_idx = idx
	return (closest_idx,closest_rect)
def maxclose_rect(rects, last_rect):
	inside_rects = []
	for rect in rects:
		centre = rect_centre(rect)
		if inside_rect(centre, last_rect):
			inside_rects.append(rect)
		else:
			inside_rects.append(None)
	if len(inside_rects) == 0:
		return closest_rect(rects, last_rect)
	else:
		ind, rec = max_rect(inside_rects)
		if ind == -1:	return closest_rect(rects, last_rect)
		else:	return (ind, rec)
def delta_rect(rect1, rect2): #calculate change between two rectangles
	delta_size = abs(rect_size(rect2) - rect_size(rect1))
	delta_pos = rect_dist(rect1, rect2)
	return (delta_pos, delta_size)
def choose_rect(rects=None, last_rect=None, method = 'closest'):
	if method=='closest': #to previous
		return closest_rect(rects, last_rect)
	elif method=='biggest':
		return max_rect(rects)
	elif method=='closestbiggest':
		return maxclose_rect(rects, last_rect)

#points
def dist(p1,p2):
	return math.sqrt( (p1[X]-p2[X])**2 + (p1[Y]-p2[Y])**2 )

#contours
def contour_centre(contour):
	xs = np.array([point[0][0] for point in contour])
	ys = np.array([point[0][1] for point in contour])
	return (np.average(xs),np.average(ys))
def contour_dist(contour1, contour2):
	contour1_centre = contour_centre(contour1)
	contour2_centre = contour_centre(contour2)
	return math.sqrt((contour1_centre[X] - contour2_centre[X])**2+(contour1_centre[Y] - contour2_centre[Y])**2)
def contour_area(contour):
	if contour == None:
		return 0
	else:
		return cv2.contourArea(contour)
def closest_contour(contours, last_contour):
	if last_contour == None:
		return max_contour(contours)
	else:
		min_dist = 999999999
		closest_contour = contours[0]
		closest_idx = 0
		for idx,contour in enumerate(contours):
			dist = contour_dist(contour, last_contour)
			if dist < min_dist:
				closest_contour = contour
				min_dist = dist
				closest_idx = idx
		return (closest_idx,closest_contour)
def max_contour(contours):
	max_contour = None
	max_idx = -1
	for idx,contour in enumerate(contours):
		if contour_area(contour) > contour_area(max_contour):
			max_contour = contour
			max_idx = idx
	return (max_idx,max_contour)
def maxclose_contour(contours, last_contour):
	if last_contour == None:
		return max_contour(contours)
	else:
		last_rect = cv2.boundingRect(last_contour)
		inside_contours = []
		for contour in contours:
			centre = contour_centre(contour)
			if inside_rect(centre, last_rect):
				inside_contours.append(contour)
			else:
				inside_contours.append(None)
		if len(inside_contours) == 0:
			return closest_contour(contours, last_contour)
		else:
			ind, con = max_contour(inside_contours)
			if ind == -1:	return closest_contour(contours, last_contour)
			else:	return (ind, con)
def choose_contour(contours, last_contour, img_rect, method = 'closestbiggest', use_rects=False):
	if use_rects:
		rects = []
		for cont in contours:
			rects.append(cv2.boundingRect(cont))
		if last_contour == None:
			last_rect = img_rect
		else:
			last_rect = cv2.boundingRect(last_contour)
		contour_index, rect = choose_rect(rects=rects, last_rect=last_rect, method = method)
		return (contour_index, contours[contour_index])
	else:
		if len(contours) == 0:
			return (0,last_contour)
		elif method=='closest': #to previous
			return closest_contour(contours, last_contour)
		elif method=='biggest':
			return max_contour(contours)
		elif method=='closestbiggest': #biggest of those whose centers are within last's bounding box
			return maxclose_contour(contours, last_contour)
def delta_contour(contour1, contour2, method=2): #calculate change between two contours
	if contour1 != None and contour2 != None:
		delta_area = abs(cv2.contourArea(contour2) - cv2.contourArea(contour1))
		delta_pos = contour_dist(contour1, contour2)
		delta_size = abs(len(contour1) - len(contour2))
		delta_sizearea = delta_size**2 * delta_area
		deltas = (delta_area, delta_pos, delta_size, delta_sizearea)
		return deltas[method]
	else:	return 0
def valid_contours(contours, cell_frames): #determines whether realistic contours
	img = cell_frames[0]
	height,width = (np.size(img, axis=0), np.size(img, axis=1))
	img_area = height * width
	areas = [contour_area(con) for con in contours]
	if np.average(areas) > 0.05*img_area:
		return True
	else:
		return False
	
def load_image(path_name):
	try:
		image = cv2.imread(path_name, 0) #0 for B&W
		height = np.size(image, axis = 0)
		width = np.size(image, axis = 1)
		return (image, height, width)
	except:
		print "Could not load image file: %s"%path_name
		exit()
def empty_img(base_img):
	return np.zeros(np.shape(base_img))
def adjust_image(img,contrast=1,brightness=0):
	return img*contrast+brightness
def img_frame(img):
	return [0,0,np.size(img, axis=1),np.size(img, axis=0)]
		
def crop(img_orig, focus_area=None):
	if focus_area == None:
		return (img_orig, np.size(img_orig, axis=0), np.size(img_orig, axis=1))
	img_cropped = img_orig[focus_area[Y1]:focus_area[Y2]]
	img_cropped = img_cropped[:,focus_area[X1]:focus_area[X2]]
	return (img_cropped, np.size(img_cropped, axis=0), np.size(img_cropped, axis=1))
def display_current(img_orig, rect=None, contour=None, focus_area=None, window='unnamed'):
	if rect is None and contour is None:
		img_orig,height,width = crop(img_orig, focus_area)
		cv2.imshow(window, img_orig)
	elif contour is not None:
		rect = cv2.boundingRect(contour)
	if rect is not None:
		img_toshow = img_orig.copy()
		pt1x = rect[X]
		pt1y = rect[Y]
		pt2x = rect[X] + rect[WIDTH]
		pt2y = rect[Y] + rect[HEIGHT]
		cv2.rectangle(img_toshow, (pt1x,pt1y),(pt2x,pt2y), (100,100,100))
		img_toshow,height,width = crop(img_toshow, focus_area)
		cv2.imshow(window,img_toshow)
def display_all(images):
	for idx,image in enumerate(images):
		if len(image) == 1:
			display_current(image[0], window = window_names[idx])
		elif len(image) == 2:
			display_current(image[0], contour = image[1], window = window_names[idx])
def pause_video():
	k = cv2.waitKey(playback_mspf)
	if k != -1:
		print "Paused"
		k = cv2.waitKey(0)
		print "Running"
def kernel(size=3):
	dims = (size,size)
	return cv2.getStructuringElement(cv2.MORPH_RECT,dims) #RECT ELLIPSE CROSS
def dilate(img, kern_size):
	if kern_size > 0:
		return cv2.dilate(img.copy(), kernel(size=kern_size))
	else:
		return img.copy()
def random_params(training_params, used):
	params = []
	while params in used or params == []:
		canny1min = training_params['canny1min']
		canny1max = training_params['canny1max']
		canny2min = training_params['canny2min']
		canny2max = training_params['canny2max']
		kern_max = training_params['kern_max']
		params = {}
		params['brightness'] = random.choice(range(0,4))
		params['contrast'] = random.choice(range(1,2))
		params['canny_thresh1'] = random.choice(range(canny1min,canny1max))
		params['canny_thresh2'] = random.choice(range(canny2min,canny2max))
		params['kern_size'] = random.choice(range(0,kern_max+1))
	return params
	
	
def get_cellframes(cell_type, cell_idx):
	cell_name = cell_type + '_' + cell_idx
	dir_name = videos_directory + cell_type + '/'		
	csvfile = open(cells_filename, "r")
	cells = csv.DictReader(csvfile)
	for cell in cells:
		if cell['label'] == cell_name:
			pic_range = [int(cell['pic_range1']),int(cell['pic_range2'])]
			if pic_range[1] - pic_range[0] != std_cell_duration:
				print "Warning: number of frames (%i) does not match standard of %i."%(pic_range[1]-pic_range[0],std_cell_duration)
			focus_area = [int(cell['focus_area_x1']),int(cell['focus_area_x2']),int(cell['focus_area_y1']),int(cell['focus_area_y2'])]
			
			#params
			cell_params = {}
			for idx,p in enumerate(default_cell_params):
				cell_params[param_names[idx]] = p
			training_params = {}
			for idx,p in enumerate(default_training_params):
				training_params[training_param_names[idx]] = p
			
			for pname in param_names:
				if cell[pname] != '':
					cell_params[pname] = int(cell[pname])
			for pname in training_param_names:
				if cell[pname] != '':
					training_params[pname] = int(cell[pname])
			
			images = []
			for img_name in os.listdir(dir_name)[pic_range[0]:pic_range[1]]:
				img_orig, height, width = crop(load_image(dir_name+img_name)[0], focus_area)
				images.append(img_orig)
			return (images,cell_params,training_params,empty_img(base_img=img_orig))
	return None
def store_params(cell_type, cell_idx, params):
	cell_name = cell_type + '_' + cell_idx
	csvfile = open(cells_filename, "r")
	cells_dr = csv.DictReader(csvfile)
	cells = list(cells_dr)
	csvfile.close()
	csvfile = open(cells_filename, 'w')
	newcells = csv.DictWriter(csvfile, fieldnames = cells_dr.fieldnames)
	newcells.writeheader()
	for cell in cells:
		if cell['label'] == cell_name:
			for pname in param_names:
				cell[pname] = params[pname]
		newcells.writerow(cell)
		
## experimental contour analyses
def contour_max_length(contour):
	maxlen = 0
	for p in contour:
		point = p[0] #opencv convention (relevant for contour hierarchies)
		for pp in contour:
			point2 = pp[0]
			if np.array_equal(point2,point):
				continue
			d = dist(point,point2)
			if d > maxlen:
				maxlen = d
	return maxlen
def contour_maxmin_lengths_through_center(contour,debug_img):
	DBusedp1max = None
	DBusedp2max = None
	DBusedp1min = None
	DBusedp2min = None
	maxlen = 0
	minlen = 99999
	centre = contour_centre(contour)
	for theta in range(1,181):
		bestp1 = None
		bestp2 = None
		bestd1 = 999 #difference b/t theta and theta
		bestd2 = 999
		#now iterate through every point on the contour until you find the one that best matches the theta in question
		for p in contour:
			point = p[0]
			dx = point[X] - centre[X]
			dy = (vid_height-point[Y]) - (vid_height-centre[Y])
			test_theta = rad2deg(math.atan2(dy,dx)) #result b/t -pi and pi
			if test_theta < 0:
				test_theta = 360 + test_theta
			#test_theta here is now an always positive degree value, starting from the positive x axis going counter clockwise, from the center of the cell to the point in question
			if abs(test_theta-theta) < bestd1:
				bestd1 = abs(test_theta - theta)
				bestp1 = point
			if abs(test_theta-(180+theta)) < bestd2:
				bestd2 = abs(test_theta - (180+theta))
				bestp2 = point
		d = dist(bestp1,bestp2)
		if d > maxlen:
			maxlen = d
			DBusedp1max = bestp1
			DBusedp2max = bestp2
		if d < minlen:
			minlen = d
			DBusedp1min = bestp1
			DBusedp2min = bestp2
	cv2.drawContours( debug_img, [contour], ALL, CD_COLOR, thickness=CD_THICKNESS+1)
	cv2.line(debug_img, (DBusedp1max[0],DBusedp1max[1]), (DBusedp2max[0],DBusedp2max[1]), (100,100,100))
	cv2.line(debug_img, (DBusedp1min[0],DBusedp1min[1]), (DBusedp2min[0],DBusedp2min[1]), (100,100,100))
	cv2.imshow('debugging', debug_img)
	cv2.waitKey(1)
	return (maxlen,minlen)
def length_ratio(contour,debug_img):
	maxlen,minlen = contour_maxmin_lengths_through_center(contour,debug_img)
	return maxlen/minlen
def contour_insvel(contour, last):
	#instantaneous velocity
	d = contour_dist(contour, last) * mmpp
	t = spf
	return d/t
def contour_circ_idx(contour):
	cen = contour_centre(contour)
	distances = np.array([dist(p[0],cen) for p in contour])
	distances = distances/max(distances)
	return np.std(distances)
def valid_theta(theta,used,thresh):
	for t in used:
		if theta < t+thresh and theta > t-thresh:
			return False
	return True
def prot_count(contour):
	## !! NOT FUNCTIONAL
	maxlen = contour_max_length(contour)
	len_thresh = maxlen/2.
	theta_thresh = 20. #degrees
	center = contour_centre(contour)
	chosen_thetas = []
	count = 0
	for p in contour:
		point = p[0]
		dx = point[X] - center[X]
		dy = (vid_height-point[Y]) - (vid_height-center[Y])
		theta = rad2deg(math.atan2(dy,dx))
		if theta < 0:
			theta = 360 + theta
		if dist(point, center) > len_thresh and valid_theta(theta,chosen_thetas,theta_thresh):
			chosen_thetas.append(theta)
			count += 1
	return count